package org.flintparticles.common.stage3d.utils
{
	import flash.display3D.Context3DBlendFactor;
	import flash.geom.Matrix3D;
	import flash.geom.Vector3D;
	
	import org.flintparticles.twoD.stage3d.images.GeomEntity2D;

	public class Stage3DMath
	{
		private var _mProjectionMatrix:Matrix3D;
		private var _mModelViewMatrix:Matrix3D;        
		private var mMatrixStack:Vector.<Matrix3D>;
		
		// construction
		
		/** Creates a new RenderSupport object with an empty matrix stack. */
		public function Stage3DMath()
		{
			mMatrixStack = new <Matrix3D>[];
			mProjectionMatrix = new Matrix3D();
			mModelViewMatrix = new Matrix3D();
			
			loadIdentity();
			setOrthographicProjection(400, 300);
		}
		
		// matrix manipulation
		
		/** Sets up the projection matrix for ortographic 2D rendering. */
		public function setOrthographicProjection(width:Number, height:Number, 
												  near:Number=-1.0, far:Number=1.0):void
		{
			var coords:Vector.<Number> = new <Number>[                
				2.0/width, 0.0, 0.0, 0.0,
				0.0, -2.0/height, 0.0, 0.0,
				0.0, 0.0, -2.0/(far-near), 0.0,
				-1.0, 1.0, -(far+near)/(far-near), 1.0                
			];
			
			mProjectionMatrix.copyRawDataFrom(coords);
		}
		
		/** Changes the modelview matrix to the identity matrix. */
		public function loadIdentity():void
		{
			mModelViewMatrix.identity();
		}
		
		/** Prepends a translation to the modelview matrix. */
		public function translateMatrix(dx:Number, dy:Number, dz:Number=0):void
		{
			mModelViewMatrix.prependTranslation(dx, dy, dz);
		}
		
		/** Prepends a rotation (angle in radians) to the modelview matrix. */
		public function rotateMatrix(angle:Number, axis:Vector3D=null):void
		{
			mModelViewMatrix.prependRotation(angle / Math.PI * 180.0, 
				axis == null ? Vector3D.Z_AXIS : axis);
		}
		
		/** Prepends an incremental scale change to the modelview matrix. */
		public function scaleMatrix(sx:Number, sy:Number, sz:Number=1.0):void
		{
			mModelViewMatrix.prependScale(sx, sy, sz);    
		}
		
		/** Prepends translation, scale and rotation of an object to the modelview matrix. */
		public function transformMatrix(object:GeomEntity2D):void
		{
			transformMatrixForObject(mModelViewMatrix, object);   
		}
		
		/** Pushes the current modelview matrix to a stack from which it can be restored later. */
		public function pushMatrix():void
		{
			mMatrixStack.push(mModelViewMatrix.clone());
		}
		
		/** Restores the modelview matrix that was last pushed to the stack. */
		public function popMatrix():void
		{
			mModelViewMatrix = mMatrixStack.pop();
		}
		
		/** Empties the matrix stack, resets the modelview matrox to the identity matrix. */
		public function resetMatrix():void
		{
			if (mMatrixStack.length != 0)
				mMatrixStack = new <Matrix3D>[];
			
			loadIdentity();
		}
		
		/** Calculates the product of modelview and projection matrix. */
		public function get mvpMatrix():Matrix3D
		{
			var mvpMatrix:Matrix3D = new Matrix3D();
			mvpMatrix.append(mModelViewMatrix);
			mvpMatrix.append(mProjectionMatrix);
			return mvpMatrix;
		}
		
		/** Prepends translation, scale and rotation of an object to a custom matrix. */
		public static function transformMatrixForObject(matrix:Matrix3D, object:GeomEntity2D):void
		{
			matrix.prependTranslation(object.x, object.y, 0.0);
			matrix.prependRotation(object.rotation / Math.PI * 180.0 , Vector3D.Z_AXIS);//object.rotation / Math.PI * 180.0
			matrix.prependScale(object.scaleX, object.scaleY, 1.0);
			matrix.prependTranslation(-object.pivot.x, -object.pivot.y, 0.0);
		}

		public function get mProjectionMatrix():Matrix3D
		{
			return _mProjectionMatrix;
		}

		public function set mProjectionMatrix(value:Matrix3D):void
		{
			_mProjectionMatrix = value;
		}

		public function get mModelViewMatrix():Matrix3D
		{
			return _mModelViewMatrix;
		}

		public function set mModelViewMatrix(value:Matrix3D):void
		{
			_mModelViewMatrix = value;
		}

		
		// other helper methods
		
		/** Sets up the default blending factors, depending on the premultiplied alpha status. */
		
	//	transfer to the rendered object
	/*	public function setDefaultBlendFactors(premultipliedAlpha:Boolean):void
		{
			var destFactor:String = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
			var sourceFactor:String = premultipliedAlpha ? Context3DBlendFactor.ONE :
				Context3DBlendFactor.SOURCE_ALPHA;
			Starling.context.setBlendFactors(sourceFactor, destFactor);
		}*/
		
		/** Clears the render context with a certain color and alpha value. */
		///transfer into rendered object///
	/*	public function clear(rgb:uint=0, alpha:Number=0.0):void
		{
			Starling.context.clear(
				Color.getRed(rgb)   / 255.0, 
				Color.getGreen(rgb) / 255.0, 
				Color.getBlue(rgb)  / 255.0,
				alpha);
		}
		
		*/
	}
}